package core

import (
	"log"
	"net/http"
	"time"

	"away.wei/minidevpos/global"
	"away.wei/minidevpos/model"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

type SocketClient struct {
	id       string
	hub      *SocketHub
	conn     *websocket.Conn
	sendChan chan model.SocketMessage
}

type SocketHub struct {
	clients map[*SocketClient]bool
	login   chan *SocketClient
	logout  chan *SocketClient
}

var hub = &SocketHub{
	login:   make(chan *SocketClient),
	logout:  make(chan *SocketClient),
	clients: make(map[*SocketClient]bool),
}

func (h *SocketHub) listen() {
	for {
		select {
		case client := <-h.login:
			h.clients[client] = true

		case client := <-h.logout:
			if _, ok := h.clients[client]; ok {
				delete(h.clients, client)
				close(client.sendChan)
			}

		case message := <-global.BroadcastChan:
			for client := range h.clients {
				select {
				case client.sendChan <- message:
				default:
					close(client.sendChan)
					delete(h.clients, client)
				}
			}
		}
	}
}

const (
	writeWait      = 10 * time.Second
	pongWait       = 60 * time.Second
	pingPeriod     = (pongWait * 9) / 10
	maxMessageSize = 512
)

func (s *SocketClient) send(data model.SocketMessage) {
	s.conn.WriteJSON(data)
}
func (c *SocketClient) readPump() {
	defer func() {
		c.hub.logout <- c
		c.conn.Close()
	}()

	c.conn.SetReadLimit(maxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(pongWait))
	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })

	for {
		var socketMsg model.SocketMessage
		err := c.conn.ReadJSON(&socketMsg)
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("error: %v", err)
			}
			break
		}

		go func() {
			for eventName, handler := range webSocketHandlers {
				if socketMsg.EventName == eventName {
					handler(socketMsg.Args)
				}
			}
		}()

	}
}

func (c *SocketClient) writePump() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.conn.Close()
	}()

	for {
		select {
		case message, ok := <-c.sendChan:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if !ok {
				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			c.send(message)
		case <-ticker.C:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}

func ginWebSocketHub(c *gin.Context) {
	id, _ := c.GetQuery("id")
	upgrader := websocket.Upgrader{
		ReadBufferSize:    4096,
		WriteBufferSize:   4096,
		EnableCompression: true,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Println(err)
		return
	}

	client := &SocketClient{id: id, hub: hub, conn: conn, sendChan: make(chan model.SocketMessage, 10)}
	client.hub.login <- client

	go client.readPump()
	go client.writePump()
}

type GinWebSocket struct{}
type GinWebSocketHandler func(string)

var webSocketHandlers = make(map[string]GinWebSocketHandler, 0)

func (*GinWebSocket) Handler(eventName string, handler GinWebSocketHandler) {
	webSocketHandlers[eventName] = handler
}
func (ginserver *GinServer) AddWebSocket(router string) {

	ginWebSocketRouter.AddRouter(GinWS)
	go hub.listen()
	ginserver.router.GET(router, ginWebSocketHub)
	log.Printf("websocket服务启动：ws://%s%s", global.Settings.ToHost(), router)
}
